﻿#region 命名空间

using System;
using System.Collections.Generic;
using System.Threading;
using Sharp.Domain.Model;
using Sharp.Infrastructure;

#endregion

namespace Sharp.Domain.Repositories
{
    /// <summary>
    /// 上下文的基类库
    /// </summary>
    public abstract class RepositoryContext : DisposableObject, IRepositoryContext
    {
        #region 私有字段

        private readonly Guid _id = IdentityGenerator.NewSecuentialGuid();

        private readonly ThreadLocal<bool> _localCommitted = new ThreadLocal<bool>(() => false);

        private readonly ThreadLocal<Dictionary<Guid, object>> _localDeletedCollection =
            new ThreadLocal<Dictionary<Guid, object>>(() => new Dictionary<Guid, object>());

        private readonly ThreadLocal<Dictionary<Guid, object>> _localModifiedCollection =
            new ThreadLocal<Dictionary<Guid, object>>(() => new Dictionary<Guid, object>());

        private readonly ThreadLocal<Dictionary<Guid, object>> _localNewCollection =
            new ThreadLocal<Dictionary<Guid, object>>(() => new Dictionary<Guid, object>());

        #endregion

        #region 方法

        /// <summary>
        ///  清除库中的上下文中的所有注册。
        /// </summary> 
        protected void ClearRegistrations()
        {
            _localNewCollection.Value.Clear();
            _localModifiedCollection.Value.Clear();
            _localDeletedCollection.Value.Clear();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _localCommitted.Dispose();
                _localDeletedCollection.Dispose();
                _localModifiedCollection.Dispose();
                _localNewCollection.Dispose();
            }
        }

        #endregion

        #region 属性

        /// <summary>
        ///     Gets an enumerator which iterates over the collection that contains all the objects need to be added to the repository.
        /// </summary>
        protected IEnumerable<KeyValuePair<Guid, object>> NewCollection
        {
            get { return _localNewCollection.Value; }
        }

        /// <summary>
        ///     Gets an enumerator which iterates over the collection that contains all the objects need to be modified in the
        ///     repository.
        /// </summary>
        protected IEnumerable<KeyValuePair<Guid, object>> ModifiedCollection
        {
            get { return _localModifiedCollection.Value; }
        }

        /// <summary>
        ///     Gets an enumerator which iterates over the collection that contains all the objects need to be deleted from the
        ///     repository.
        /// </summary>
        protected IEnumerable<KeyValuePair<Guid, object>> DeletedCollection
        {
            get { return _localDeletedCollection.Value; }
        }

        #endregion

        #region IRepositoryContext Members

        /// <summary>
        ///     Gets the ID of the repository context.
        /// </summary>
        public Guid Id
        {
            get { return _id; }
        }

       /// <summary>
        /// 注册一个即将添加的对象到下上文
       /// </summary>
       /// <typeparam name="TAggregateRoot"></typeparam>
       /// <param name="obj"></param>
        public virtual void RegisterNew<TAggregateRoot>(TAggregateRoot obj) where TAggregateRoot : class, IAggregateRoot
        {
            if (obj.Id.Equals(Guid.Empty))
                throw new ArgumentException("The ID of the object is empty.", "obj");
            //if (modifiedCollection.ContainsKey(obj.ID))
            if (_localModifiedCollection.Value.ContainsKey(obj.Id))
                throw new InvalidOperationException(
                    "The object cannot be registered as a new object since it was marked as modified.");
            if (_localNewCollection.Value.ContainsKey(obj.Id))
                throw new InvalidOperationException("The object has already been registered as a new object.");

            _localNewCollection.Value.Add(obj.Id, obj);
            _localCommitted.Value = false;
        }

        /// <summary>
        /// 注册一个即将修改的对象到下上文
        /// </summary>
        /// <typeparam name="TAggregateRoot"></typeparam>
        /// <param name="obj"></param>
        public virtual void RegisterModified<TAggregateRoot>(TAggregateRoot obj)
            where TAggregateRoot : class, IAggregateRoot
        {
            if (obj.Id.Equals(Guid.Empty))
                throw new ArgumentException("The ID of the object is empty.", "obj");
            if (_localDeletedCollection.Value.ContainsKey(obj.Id))
                throw new InvalidOperationException(
                    "The object cannot be registered as a modified object since it was marked as deleted.");
            if (!_localModifiedCollection.Value.ContainsKey(obj.Id) && !_localNewCollection.Value.ContainsKey(obj.Id))
                _localModifiedCollection.Value.Add(obj.Id, obj);
            _localCommitted.Value = false;
        }

        /// <summary>
        /// 注册一个即将删除的对象到下上文
        /// </summary>
        /// <typeparam name="TAggregateRoot"></typeparam>
        /// <param name="obj"></param>
        public virtual void RegisterDeleted<TAggregateRoot>(TAggregateRoot obj)
            where TAggregateRoot : class, IAggregateRoot
        {
            if (obj.Id.Equals(Guid.Empty))
                throw new ArgumentException("The ID of the object is empty.", "obj");
            if (_localNewCollection.Value.ContainsKey(obj.Id))
            {
                if (_localNewCollection.Value.Remove(obj.Id))
                    return;
            }
            bool removedFromModified = _localModifiedCollection.Value.Remove(obj.Id);
            bool addedToDeleted = false;
            if (!_localDeletedCollection.Value.ContainsKey(obj.Id))
            {
                _localDeletedCollection.Value.Add(obj.Id, obj);
                addedToDeleted = true;
            }
            _localCommitted.Value = !(removedFromModified || addedToDeleted);
        }

        /// <summary>
        /// 应用更改
        /// </summary>
        /// <typeparam name="TAggregateRoot"></typeparam>
        /// <param name="original"></param>
        /// <param name="current"></param>
        public virtual void ApplyCurrentValues<TAggregateRoot>(TAggregateRoot original, TAggregateRoot current)
            where TAggregateRoot : class
        {
        }

        #endregion

        #region IUnitOfWork Members

        /// <summary>
        /// 是否已经提交工作单元
        /// </summary>
        public bool Committed
        {
            get { return _localCommitted.Value; }
            set { _localCommitted.Value = value; }
        }

        /// <summary>
        /// 提交工作单元
        /// </summary>
        public abstract void Commit();

        /// <summary>
        /// 回滚工作单元
        /// </summary>
        public abstract void Rollback();

        #endregion
    }
}